#ifndef __THREADSAFEQUEUE_H__
#define __THREADSAFEQUEUE_H__

#include <deque>
#include <mutex>
#include <condition_variable>
#include <chrono>

namespace NetWork {
    template<typename T>
    using DeQueue = std::deque<T, std::allocator<T>>;

    template<typename T>
    class ThreadSafeQueue {
    private:
        using Second = std::chrono::seconds;

    private:
        DeQueue<T> Queue;
        std::mutex Mutex;
        std::condition_variable Productor;  //use to push
        std::condition_variable Consumer;   //use to pop

        size_t MaxSize;
        bool bEndOfLife;

    public:
        ThreadSafeQueue(size_t maxSize = 1024)
        : MaxSize(maxSize), bEndOfLife(false)
        {}

        //多线程中, 析构函数非常重要
        ~ThreadSafeQueue() {
            {
                std::lock_guard<std::mutex> lock(Mutex);
                bEndOfLife = true;
                Queue.clear();
            }

            Productor.notify_all();
            Consumer.notify_all();
        }
    public:
        void Push(const T& t) {
            std::unique_lock<std::mutex> lock(Mutex);

            //若条件不成立, 解锁 mutex, 并且进行等待
            Productor.wait(lock, [MaxSize = MaxSize, DeQueueSize = Queue.size()](){
                return DeQueueSize < MaxSize;
            });

            if (bEndOfLife)
                return;

            Queue.push_back(t);
            Consumer.notify_one();
        }

        void Pop() {
            std::unique_lock<std::mutex> lock(Mutex);

            Consumer.wait(lock, [DequeueSize = Queue.size(), bEndOfLife = bEndOfLife](){
                return DequeueSize > 0 || bEndOfLife;
            });

            if (bEndOfLife)
                return;

            Queue.pop_front();
            Productor.notify_one();
        }

        bool Pop(size_t timeout) {
            std::unique_lock<std::mutex> lock(Mutex);
            // wait_for 等待秒数, 超时则 return false
            if(!Consumer.wait_for(
                lock, 
                Second(timeout),
                [DequeueSize = Queue.size(), bEndOfLife = bEndOfLife](){
                    return DequeueSize > 0 || bEndOfLife;
                }))
                return false; 

            if (bEndOfLife)
                return true;

            Queue.pop_front();
            Productor.notify_one();

            return true;
        }

        T Front() {
            std::lock_guard<std::mutex> lock(Mutex);
            return Queue.front();
        }

        size_t Size() {
            std::lock_guard<std::mutex> lock(Mutex);
            return Queue.size();
        }

        bool Empty() {
            std::lock_guard<std::mutex> lock(Mutex);
            return Queue.empty();
        }

        void Flush() {
            std::lock_guard<std::mutex> lock(Mutex);
            Consumer.notify_one();
        }
    };
}
#endif